"""
串口通信管理模块 - 负责串口连接、数据收发和协议解析
"""
import serial
import serial.tools.list_ports
import threading
import time
import json
import binascii
from typing import Optional, List
from core.event_bus import event_bus, EventType
from core.config import Config

class SerialManager:
    """串口通信管理器"""
    
    def __init__(self):
        self.serial_port: Optional[serial.Serial] = None
        self.is_connected = False
        self.is_receiving = False
        self.receive_thread: Optional[threading.Thread] = None
        self.data_buffer = ""
        
    def get_available_ports(self) -> List[str]:
        """获取可用串口列表"""
        ports = serial.tools.list_ports.comports()
        return [port for port, desc, hwid in sorted(ports)]
    
    def connect(self, port: str, baudrate: int = Config.DEFAULT_BAUDRATE) -> bool:
        """连接串口"""
        try:
            if self.is_connected:
                self.disconnect()
                
            self.serial_port = serial.Serial(
                port=port, 
                baudrate=baudrate, 
                timeout=Config.SERIAL_TIMEOUT
            )
            self.is_connected = True
            self._clear_buffers()
            # event_bus.emit(EventType.SERIAL_CONNECTED, port)
            return True
        
        except Exception as e:
            print(f"串口连接失败：{e}")
            return False
    
    def disconnect(self) -> bool:
        """断开串口连接"""
        try:
            self.stop_receiving()
            
            if self.serial_port and self.serial_port.is_open:
                self.serial_port.close()
                
            self.is_connected = False
            self._clear_buffers()
            # event_bus.emit(EventType.SERIAL_DISCONNECTED)
            return True
            
        except Exception as e:
            print(f"串口断开失败：{e}")
            return False
    
    def start_receiving(self) -> bool:
        """开始接收数据"""
        if not self.is_connected or self.is_receiving:
            return False
            
        self.is_receiving = True
        self.receive_thread = threading.Thread(target=self._receive_loop)
        self.receive_thread.start()
        return True
    
    def stop_receiving(self):
        """停止接收数据"""
        self.is_receiving = False
        if self.receive_thread and self.receive_thread.is_alive():
            try:
                self.receive_thread.join(timeout=2.0)
            except Exception as e:
                print(f"停止接收线程失败：{e}")
            finally:
                self.receive_thread = None
    
    def send_data(self, cmd_type: str, control_type: str = None, 
                  value = None, screen_id: str = '0000', 
                  control_id: str = '0012') -> bool:
        """发送数据到设备"""
        if not self.is_connected:
            return False
            
        try:
            # 帧头
            frame_header = b'\xEE'
            if cmd_type == Config.CMD_TYPES['SET_INTERVAL'] and control_type == Config.CTRL_MSG_TYPES['CONTROL_INPUT']:
                # 处理设置间隔命令
                if isinstance(value, int):
                    text_value = str(value)
                elif isinstance(value, str):
                    # text_value = value
                    text_value = ''.join(ch for ch in value if ch.isprintable())
                elif isinstance(value, dict):
                    try:
                        if 'value' in value and isinstance(value['value'], (str, int, float)):
                            text_value = str(value['value'])
                        else:
                            # ensure_ascii 保证可按 ASCII 直接十六进制编码
                            text_value = json.dumps(value, ensure_ascii=True)
                    except Exception:
                        return False
                else:
                    return False
                
                # 将 ASCII 文本编码为十六进制参数
                text_value_hex = text_value.encode('ascii').hex()
                # 为了兼容更弱的设备端，字典/0015 控件不附加 00 终止符
                add_terminator = not (isinstance(value, dict) or control_id == '0015')
                
                if screen_id == '0000' and control_id in ('0012', '0013', '0014', '0015'):
                    params_upload = (
                        bytes.fromhex(cmd_type) + 
                        bytes.fromhex(screen_id) + 
                        bytes.fromhex(control_id) + 
                        bytes.fromhex(control_type) +
                        bytes.fromhex(text_value_hex) 
                    )
                    if add_terminator:
                        params_upload += bytes.fromhex('00')
                    
                    frame_end = b'\xFF\xFC\xFF\xFF'
                    frame_data = frame_header + params_upload + frame_end
                    
                    self.serial_port.write(frame_data)
                    return True
                    
            # 处理其他命令类型
            elif cmd_type in Config.CMD_TYPES.values():
                # 发送RTC 校准时间
                if cmd_type == Config.CMD_TYPES['SET_TIME']:
                    # 时间设置命令
                    params_upload = (
                        bytes.fromhex(cmd_type) +
                        bytes.fromhex(value) 
                    )
                # 发送获取设备RTC/取消连接/下载/实时数据
                else:   # 'dd','e7','e8','e9' 
                    # print(f"发送命令：{cmd_type}")
                    params_upload = (
                        bytes.fromhex(cmd_type)   
                    )
                frame_end = b'\xFF\xFC\xFF\xFF'
                frame_data = frame_header + params_upload + frame_end
                self.serial_port.write(frame_data)

                return True
                
            return False
            
        except Exception as e:
            print(f"数据发送失败：{e}")
            return False
    
    def _receive_loop(self):
        """数据接收循环"""
        while self.is_receiving and self.serial_port:
            try:
                if self.serial_port.in_waiting > 0:
                    rec_data = self.serial_port.read(self.serial_port.in_waiting)
                    # 先向总线广播原始字节数据，供下载管理器使用（文件列表/文件内容解析）
                    event_bus.emit(EventType.SERIAL_DATA_RECEIVED, rec_data)
                    frames = self._parse_frames(rec_data)
                    
                    if frames:
                        for frame in frames:
                            # print(f"接收到数据帧: {frame}")
                            event_bus.emit(EventType.SERIAL_DATA_RECEIVED, frame)

                else:
                    time.sleep(0.01)
                    
            except Exception as e:
                print(f"数据接收错误：{e}")
                break
    
    def _parse_frames(self, data: bytes) -> List[str]:
        """解析数据帧"""
        datastr = binascii.hexlify(data).decode('utf-8')
        self.data_buffer += datastr
        
        frames = []
        while True:
            head_index = self.data_buffer.find(Config.FRAME_START)
            if head_index == -1:
                break
            # 从帧头位置开始寻找帧尾  
            search_start = head_index + len(Config.FRAME_START)
            end_index = self.data_buffer.find(Config.FRAME_END, search_start)
            
            if end_index == -1:
                self.data_buffer = self.data_buffer[head_index:]
                break

            # 计算帧尾结束位置   
            end_index += len(Config.FRAME_END)
            # 提取完整帧
            frame = self.data_buffer[head_index:end_index]
            #验证帧的最小长度
            #最小帧长度检查 (帧头2+命令2+控制2+画面4+控件4+帧尾8)
            if len(frame) >= Config.MIN_FRAME_LENGTH and self._validate_frame(frame):
                frames.append(frame)
            
            self.data_buffer = self.data_buffer[end_index:]
            
            if len(self.data_buffer) > Config.MAX_BUFFER_SIZE:
                self.data_buffer = ""
                
        return frames
    
    def _validate_frame(self, frame: str) -> bool:
        """验证帧结构的有效性"""
        # 检查帧头和帧尾
        if not frame.startswith(Config.FRAME_START) or not frame.endswith(Config.FRAME_END):
            return False
        # 检查最小长度（帧头2 + 命令类型2 + 控制消息2 + 画面ID4 + 控件ID4 + 帧尾8 = 22）  
        if len(frame) < Config.MIN_FRAME_LENGTH:
            return False
        # 提取命令类型  
        cmd_type = frame[2:4]
        if cmd_type not in Config.CMD_TYPES.values():
            return False
        # 提取控制消息类型
        ctrl_msg = frame[4:6]
        if ctrl_msg not in Config.CTRL_MSG_TYPES.values():
            return False
        return True
    
    def _clear_buffers(self):
        """清空缓冲区"""
        self.data_buffer = ""
        if self.serial_port:
            try:
                self.serial_port.reset_input_buffer()
                self.serial_port.reset_output_buffer()
            except:
                pass